#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <stdbool.h>
#include "MQTTClient.h"
#include "shmem.h"
#include "msg_queue_peer.h"
#include "cJSON.h"

// ======================== 宏和定义 ========================

#define N 32
#define SHM_SIZE 4096 // 共享内存大小
// MQTT连接参数
#define ADDRESS "tcp://192.168.31.153:1883" // MQTT代理地址
#define CLIENTID "ExampleClientSub"    // 客户端ID
#define PUB_TOPIC "cloud"                 // 发布主题
#define SUB_TOPIC "cldw"               // 订阅主题
#define QOS 1                          // 服务质量等级
#define TIMEOUT 10000L                 // 超时时间(毫秒)

// mqtt服务器初始化配置
volatile MQTTClient_deliveryToken deliveredtoken;
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc;
int ch;
// 设备数据类型
typedef union
{
    int b_val;   // 布尔型
    int i_val;   // 整型
    float f_val; // 浮点型
} val_t;

// 共享内存节点结构
typedef struct
{
    int key;       // 节点唯一标识
    int type;      // 数据类型 (1:bool, 2:int, 3:float)
    int dev_type;  // 设备类型
    val_t old_val; // 旧值
    val_t new_val; // 新值
    int ret;       // 状态码
} std_node;

static struct shm_param para; // 共享内存所需
static int *total;            // 指向共享内存中数据节点总个数
static std_node *node_arr;    // 指向共享内存中节点缓存数组头

// 消息队列结构体
typedef struct
{
    long mtype;  // 控制的设备modbus=1,stm32=2
    int key;     // 要控制的设备key
    val_t value; // 要设置的值
    int type;    // 消息类型
} control_msg;

// ======================== 函数声明 ========================
void *handler_report(void *arg); // 上传数据线程
void delivered(void *context, MQTTClient_deliveryToken dt);
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message);
void connlost(void *context, char *cause);
// ======================== 主函数 ========================

int main(int argc, char const *argv[])
{
    // ---------- 1. 建立mqtt服务器连接 ----------

    MQTTClient_create(&client, ADDRESS, CLIENTID,
                      MQTTCLIENT_PERSISTENCE_NONE, NULL); // 创建客户端
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered); // 回调函数

    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) // 连接
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
           "Press Q<Enter> to quit\n\n",
           SUB_TOPIC, CLIENTID, QOS);
    MQTTClient_subscribe(client, SUB_TOPIC, QOS); // 订阅QT客户端

    // ---------- 2. 打开共享内存 ----------
    // 初始化共享内存
    int shmret = -1;
    shmret = shm_init(&para, "shm_test", SHM_SIZE);
    if (shmret < 0)
    {
        return -1;
    }
    void *node_p = shm_getaddr(&para);
    if (node_p == NULL)
    {
        printf("shm_getaddr err\n");
        return -1;
    }
    memset(node_p, 0, SHM_SIZE);
    total = (int *)node_p;                         // 指向共享内存头
    node_arr = (std_node *)(node_p + sizeof(int)); // 指向共享内存结构体数组头
    printf("共享内存初始完成\n");

    // ---------- 3. 创建上传和接收线程 ----------

    pthread_t reportid; // 上报线程pid
    if (pthread_create(&reportid, NULL, handler_report, NULL) != 0)
    {
        perror("report_pthread_create err");
        return -1;
    }

    pthread_join(reportid,NULL);
    MQTTClient_unsubscribe(client, SUB_TOPIC);
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return 0;
}

// ======================== 函数定义 ========================

// 数据上报线程函数
void *handler_report(void *arg)
{
    // 创建cjson树，用来替换
    cJSON *root = cJSON_CreateObject(); // 最外层{}

    cJSON_AddItemToObject(root, "fan", cJSON_CreateTrue());                 // key == 13
    cJSON_AddItemToObject(root, "tem", cJSON_CreateNumber(26));             // key ==  4
    cJSON_AddItemToObject(root, "hum", cJSON_CreateNumber(88));             // key == 16
    cJSON_AddItemToObject(root, "infrared", cJSON_CreateNumber(1));         // key ==  1
    cJSON_AddItemToObject(root, "flame", cJSON_CreateNumber(0));            // key ==  8
    cJSON_AddItemToObject(root, "light", cJSON_CreateNumber(100));          // key ==  6
    cJSON_AddItemToObject(root, "irrigated", cJSON_CreateTrue());           // key == 104
    cJSON_AddItemToObject(root, "air-switch", cJSON_CreateTrue());          // key == 102
    cJSON_AddItemToObject(root, "Disinfection-switch", cJSON_CreateTrue()); // key == 105
    cJSON_AddItemToObject(root, "mo-switch", cJSON_CreateTrue());           // key == 103
    cJSON_AddItemToObject(root, "photoelectricity", cJSON_CreateTrue());    // key == 5
    cJSON_AddItemToObject(root, "lamp", cJSON_CreateTrue());                // key == 11

    // 读取共享内存数据并上传
    while (1)
    {
        sleep(1);
        for (int i = 0; i < *total; ++i)
        {
            if (node_arr[i].key == 1)
            {
                cJSON_ReplaceItemInObject(root, "infrared", cJSON_CreateNumber(node_arr[i].new_val.i_val));
            }
            else if (node_arr[i].key == 4)
            {
                cJSON_ReplaceItemInObject(root, "tem", cJSON_CreateNumber(node_arr[i].new_val.f_val));
            }
            else if (node_arr[i].key == 5)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "photoelectricity", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "photoelectricity", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 6)
            {
                cJSON_ReplaceItemInObject(root, "light", cJSON_CreateNumber(node_arr[i].new_val.i_val));
            }
            else if (node_arr[i].key == 8)
            {
                cJSON_ReplaceItemInObject(root, "flame", cJSON_CreateNumber(node_arr[i].new_val.i_val));
            }
            else if (node_arr[i].key == 11)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "lamp", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "lamp", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 13)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "fan", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "fan", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 16)
            {
                cJSON_ReplaceItemInObject(root, "hum", cJSON_CreateNumber(node_arr[i].new_val.f_val));
            }
            else if (node_arr[i].key == 102)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "air-switch", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "air-switch", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 103)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "mo-switch", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "mo-switch", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 104)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "irrigated", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "irrigated", cJSON_CreateFalse());
                }
            }
            else if (node_arr[i].key == 105)
            {
                if (node_arr[i].new_val.b_val == 1)
                {
                    cJSON_ReplaceItemInObject(root, "Disinfection-switch", cJSON_CreateTrue());
                }
                else
                {
                    cJSON_ReplaceItemInObject(root, "Disinfection-switch", cJSON_CreateFalse());
                }
            }
        }
        char *reportbuf = cJSON_Print(root); // 转字符串
        // mqtt发送
        pubmsg.payload = reportbuf;
        pubmsg.payloadlen = (int)strlen(reportbuf);
        pubmsg.qos = QOS;
        pubmsg.retained = 0;
        MQTTClient_publishMessage(client, PUB_TOPIC, &pubmsg, &token);
        rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
        printf("cloud_report\n");
        free(reportbuf);
    }
    cJSON_Delete(root);
}

void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}
// 消息到达回调函数
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    printf("创建树\n");
    cJSON *msg_json = cJSON_Parse(message->payload); // 将消息转为json格式
    control_msg msg_send;
    // 接收val值
    int control_vali;   // bool和整形
    float control_valf; // 浮点型
    if (cJSON_HasObjectItem(msg_json, "fan") != 0)
    {
        msg_send.mtype = 2;
        msg_send.key = 13;
        msg_send.type = 1;
        cJSON *val = cJSON_GetObjectItem(msg_json, "fan");
        if (val->type == cJSON_True)
        {
            msg_send.value.b_val = 1;
        }
        else
        {
            msg_send.value.b_val = 0;
        }
        printf("放消息队列\n");
        if (msg_queue_send("device_control", &msg_send, sizeof(msg_send), 0) < 0)
        {
            printf("msg_queue_send error\n");
            return -1;
        }
        printf("msg_send.mtype=%ld,msg_send.value=%d\n", msg_send.mtype, msg_send.value.b_val);
    }
    else if (cJSON_HasObjectItem(msg_json, "lamp") != 0)
    {
        msg_send.mtype = 2;
        msg_send.key = 11;
        msg_send.type = 1;
        cJSON *val = cJSON_GetObjectItem(msg_json, "fan");
        if (val->type == cJSON_True)
        {
            msg_send.value.b_val = 1;
        }
        else
        {
            msg_send.value.b_val = 0;
        }
        if (msg_queue_send("device_control", &msg_send, sizeof(msg_send), 0) < 0)
        {
            printf("msg_queue_send error\n");
            return -1;
        }
        printf("msg_send.mtype=%ld,msg_send.value=%d\n", msg_send.mtype, msg_send.value.b_val);
    }
    else if (cJSON_HasObjectItem(msg_json, "irrigated") != 0)
    {
        msg_send.mtype = 1;
        msg_send.key = 104;
        msg_send.type = 1;
        cJSON *val = cJSON_GetObjectItem(msg_json, "fan");
        if (val->type == cJSON_True)
        {
            msg_send.value.b_val = 1;
        }
        else
        {
            msg_send.value.b_val = 0;
        }
        if (msg_queue_send("device_control", &msg_send, sizeof(msg_send), 0) < 0)
        {
            printf("msg_queue_send error\n");
            return -1;
        }
        printf("msg_send.mtype=%ld,msg_send.value=%d\n", msg_send.mtype, msg_send.value.b_val);
    }

    cJSON_Delete(msg_json);
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}